/**
 * @file FORD_VFD.h
 * @brief This header file defines the FORD_VFD class, which is used to control the display of a specific device.
 *
 * This class provides a series of methods for displaying spectrum, numbers, symbols, dot matrix and other information, and also supports animation effects.
 *
 * @author Shihua Feng
 * @date February 18, 2025
 */

#ifndef _FORD_VFD_H_
#define _FORD_VFD_H_

#include <cmath>
#include <esp_wifi.h>
#include <driver/spi_master.h>
#include <driver/gpio.h>
#include <esp_log.h>
#include "spectrumdisplay.h"

#define CHAR_SIZE (62 + 1)
#define FORD_WIDTH 142
#define FORD_HEIGHT 16
#define NUM_COUNT (9)

class FORD_VFD
{
public:
    typedef struct
    {
        int byteIndex; // Byte index
        int bitMask;  // Bit index
    } SymbolPosition;

    typedef enum
    {
        IDLE = -1,
        CONTENT,
        FFT
    } Mode;

    typedef enum
    {
        NONE = -1,
        CLOCKWISE,
        ANTICLOCKWISE,
        UP2DOWN,
        DOWN2UP,
        LEFT2RT,
        RT2LEFT,
        MAX
    } NumAni;

    typedef enum
    {
        COLON1, // 冒号1
        COLON2, // 冒号2
        POINT1, // 点1
        FM,     // fm
        AM,     // am
        DAB,    // dab
        NUM1,   // 1
        NUM2,   // 2
        TA,     // ta
        TP,     // tp
        BT,     // 蓝牙
        AUX,    // 耳机
        IPOD,   // ipod
        UDISK,  // U盘
        CD0,    // CD
        CD1,    // CD
        CD2,    // CD
        CD3,    // CD
        SYMBOL_MAX
    } Symbols;

    typedef struct
    {
        char current_content;
        char last_content;
        int animation_step;
        NumAni animation_type;
    } NumberData;

public:
    FORD_VFD(gpio_num_t din, gpio_num_t clk, gpio_num_t cs, spi_host_device_t spi_num);
    FORD_VFD(spi_device_handle_t spi_device);
    void draw_point(int x, int y, uint8_t dot, Mode mode = CONTENT);
    void clear();
    void find_enum_code(Symbols flag, int *byteIndex, int *bitMask);
    void symbolhelper(Symbols symbol, bool is_on);

    void charhelper(int index, char ch);
    void charhelper(int index, uint8_t code);
    void setsleep(bool en);
    void refrash(uint8_t *gram, int size);

    void time_blink();
    void number_show(int start, char *buf, int size, NumAni ani = CLOCKWISE);
    uint8_t contentgetpart(uint8_t raw, uint8_t before_raw, uint8_t mask);
    void test();
    void setmode(Mode mode)
    {
        _mode = mode;
    }
    SpectrumDisplay *_spectrum;

protected:
    uint8_t find_hex_code(char ch);
    void write_data8(uint8_t dat);
    void write_data8(uint8_t *dat, int len);
    void setbrightness(uint8_t brightness);

private:
    Mode _mode = CONTENT;
    gpio_num_t _cs;
    const uint8_t init_data_block0[102] = {0};
    const uint8_t init_data_block1[4] = {0x01, 0xa8, 0x4c, 0x80};
    const uint8_t init_data_block2[4] = {0x81, 0xfc, 0x00, 0x00};
    const uint8_t init_data_block3[2] = {0x41, 0xf4};
    const uint8_t init_data_block4[2] = {0xcf, 0x00};
    const uint8_t init_data_block5[153 + 154] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xc0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
        0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00,
        0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
        0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00,
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xfe, 0x00, 0x00, 0x00, 0x00,
        0xc0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
        0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00,
        0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00};
    const uint8_t init_data_block7[2] = {0x91, 0x00};
    const uint8_t init_data_block8[2] = {0x11, 0x20};
    unsigned char gram[818] = {
        0x6F,
        0x00,
    };
    // Hexadecimal code corresponding to each character
    // !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz
    const uint8_t hex_codes[CHAR_SIZE] = {
        0,
        0,    // !
        0,    // "
        0,    // #
        0,    // $
        0,    // %
        0,    // &
        0,    // '
        0,    // (
        0,    // )
        0x61, // *
        0,    // +
        0,    // ,
        0,    // -
        0,    // .
        0,    // /
        0x3f, // 0
        0x06, // 1
        0x5b, // 2
        0x4f, // 3
        0x66, // 4
        0x6d, // 5
        0x7d, // 6
        0x07, // 7
        0x7f, // 8
        0x6f, // 9
        0,    // :
        0,    // ;
        0,    // <
        0,    // =
        0,    // >
        0,    // ?
        0,    // @
        0x77, // A
        0x7c, // B
        0x39, // C
        0x5e, // D
        0x79, // E
        0x71, // F
        0x6f, // G
        0x76, // H
        0x06, // I
        0x0e, // J
        0x74, // K
        0x38, // L
        0x36, // M
        0x54, // N
        0x3f, // O
        0x73, // P
        0x67, // Q
        0x50, // R
        0x6d, // S
        0x78, // T
        0x3e, // U
        0x3e, // V
        0x36, // W
        0x76, // X
        0x6e, // Y
        0x5b  // Z
    };

    SymbolPosition symbolPositions[100] = {
        {272, 0x20}, // 冒号1
        {816, 0x20}, // 冒号2
        {270, 0x40}, // 点1
        {513, 1},    // fm
        {511, 0x20}, // am
        {513, 0x40}, // dab
        {513, 2},    // 1
        {513, 4},    // 2
        {513, 8},    // ta
        {513, 0x10}, // tp
        {513, 0x20}, // 蓝牙
        {511, 0x40}, // 耳机
        {511, 0x80}, // ipod
        {510, 1},    // U盘
        {510, 0x10}, // CD
        {510, 4},    // CD
        {510, 8},    // CD
        {510, 2},    // CD
    };
    uint8_t dimming = 0;
    spi_device_handle_t spi_device_;
    NumberData currentData[NUM_COUNT] = {0};
    void init();
    void init_task();
    void contentanimate();
    uint8_t get_group(int x, uint8_t dot, uint8_t group, bool isOdd);
};

#endif